<script lang="ts">
import { h, defineComponent, computed, type PropType, type VNode, type CSSProperties } from 'vue';
import { Handle, Position, type NodeProps } from '@vue-flow/core';
import type { UniversalFlowNodeData, NodeSemanticType } from '@/models/universalFlow';
import { useUniversalVisualizerSettingsStore } from '@/stores/universalVisualizerSettingsStore';
import type { AttributeDisplayMode, AttributeSeparatorStyle } from '@/stores/universalVisualizerSettingsStore';
import { ElIcon } from 'element-plus';
import {
  Document,
  Folder,
  Files,
  CollectionTag,
  ChatDotSquare,
  QuestionFilled
} from '@element-plus/icons-vue';
import {
  semanticTypeColorCodes,
  jdfCoreTagColorCodes,
  defaultElementObjectColor,
  levelColorPalette
} from '@/config';
import tinycolor from 'tinycolor2';

// Helper function for truncating text
const truncate = (text: string | undefined | null, maxLength: number): string => {
  const str = String(text ?? '');
  return str.length > maxLength ? str.substring(0, maxLength - 1) + '…' : str;
};

export default defineComponent({
  name: 'RenderFunctionUniversalNode',
  // --- Props Definition (same as UniversalNode.vue) ---
  props: {
    // Props injected by Vue Flow
    id: { type: String, required: true },
    type: { type: String, default: 'universal' },
    selected: { type: Boolean, default: false },
    dragging: { type: Boolean, default: false },
    position: { type: Object as PropType<{ x: number; y: number }>, required: true },
    dimensions: { type: Object as PropType<{ width: number; height: number }>, default: () => ({ width: 0, height: 0 }) },
    label: [String, Object, Function] as PropType<string | VNode | (() => VNode)>,
    isValidTargetPos: Function as PropType<(connection: any) => boolean>,
    isValidSourcePos: Function as PropType<(connection: any) => boolean>,
    parentNode: String,
    zIndex: Number,
    targetPosition: String as PropType<Position>,
    sourcePosition: String as PropType<Position>,
    dragHandle: String,
    data: { type: Object as PropType<UniversalFlowNodeData>, required: true },
    events: Object,
    hidden: Boolean,
  },
  emits: ['updateNodeInternals'], // Standard Vue Flow emit

  setup(props, { emit }) {
    const visualizerSettings = useUniversalVisualizerSettingsStore();

    // --- Computed properties (replicated from UniversalNode.vue) ---

    const nodeTypeClass = computed(() => `node-type-${props.data.nodeSemanticType}`);

    const nodeIcon = computed(() => {
      // Use shallowRef or markRaw if needed for components, h() handles it
      switch (props.data.nodeSemanticType) {
        case 'root': return Document;
        case 'element': return Folder;
        case 'object': return Folder;
        case 'array': return Files;
        case 'value': return CollectionTag;
        case 'comment': return ChatDotSquare;
        default: return QuestionFilled;
      }
    });

    const displayValue = computed(() => {
      if (props.data.nodeSemanticType === 'value') {
        if (props.data.value === null) return 'null';
        if (typeof props.data.value === 'boolean') return props.data.value ? 'true' : 'false';
        if (props.data.value instanceof Date) return props.data.value.toISOString();
        const valStr = String(props.data.value);
        return truncate(valStr, 30);
      }
      if (props.data.nodeSemanticType === 'comment' && props.data.metadata?.comment) {
        const commentStr = Array.isArray(props.data.metadata.comment)
            ? props.data.metadata.comment[0]
            : props.data.metadata.comment;
        return truncate(commentStr, 30);
      }
      return null;
    });

    const nodeBackgroundColor = computed<string>(() => {
      const mode = visualizerSettings.coloringMode;
      const type = props.data.nodeSemanticType;
      const name = props.data.name;
      const level = props.data.level ?? 0;
      const format = props.data.format;
      const fallbackColor = defaultElementObjectColor;

      const adjustColorByLevel = (baseColor: string, currentLevel: number): string => {
        try {
          const lightenAmount = Math.min(currentLevel * 3, 30);
          return tinycolor(baseColor).lighten(lightenAmount).toString();
        } catch (e) {
          return baseColor;
        }
      };
      const getJdfKeywordColor = (): string | undefined => (name && (format === 'xml' || format === 'unknown')) ? jdfCoreTagColorCodes[name] : undefined;

      switch (mode) {
        case 'semantic': return semanticTypeColorCodes[type] || fallbackColor;
        case 'level': return (levelColorPalette.length > 0) ? levelColorPalette[level % levelColorPalette.length] : fallbackColor;
        case 'single-structure': return (type === 'element' || type === 'object') ? (visualizerSettings.singleStructureColorValue || fallbackColor) : (semanticTypeColorCodes[type] || fallbackColor);
        case 'single-true': return (type === 'root' && semanticTypeColorCodes['root']) ? semanticTypeColorCodes['root'] : (visualizerSettings.trueSingleColorValue || '#d1d5db');
        case 'jdf-keyword': return (type === 'element' || type === 'object') ? (getJdfKeywordColor() || fallbackColor) : (semanticTypeColorCodes[type] || fallbackColor);
        case 'jdf-keyword-level':
        default:
          if (type === 'element' || type === 'object') {
            const baseColor = getJdfKeywordColor() || fallbackColor;
            return adjustColorByLevel(baseColor, level);
          }
          return semanticTypeColorCodes[type] || fallbackColor;
      }
    });

    const contrastingTextColor = computed<string>(() => {
      const bgColor = nodeBackgroundColor.value;
      if (!bgColor) return '#1f2937';
      try {
        return tinycolor(bgColor).isLight() ? '#1f2937' : 'white';
      } catch (e) {
        return '#1f2937';
      }
    });

    const attributeCount = computed(() => {
      if (!props.data.attributes) return 0;
      return typeof props.data.attributes === 'object' && props.data.attributes !== null
          ? Object.keys(props.data.attributes).length
          : 0;
    });

    const canShowAttributes = computed(() => {
      return (props.data.nodeSemanticType === 'element' || props.data.nodeSemanticType === 'object') && attributeCount.value > 0;
    });

    // --- Render Function ---
    return () => {
      const { data, selected } = props;
      const {
        attributeDisplayMode,
        attributeSeparatorStyle,
        normalizeAppearanceWithAttrs
      } = visualizerSettings;

      // --- Calculate dynamic styles for the root node ---
      const rootNodeStyles: CSSProperties = {
        backgroundColor: nodeBackgroundColor.value,
        color: contrastingTextColor.value,
      };
      // Apply height normalization padding if needed
      if (normalizeAppearanceWithAttrs && attributeCount.value === 0) {
        // This compensates for the wrapper (margin + padding + border) that exists in nodes *with* attributes
        const wrapperVerticalSpace = (6 + 6 + (attributeSeparatorStyle === 'none' ? 0 : 1)); // margin + padding + border
        rootNodeStyles.paddingBottom = `${wrapperVerticalSpace}px`;
      } else {
        rootNodeStyles.paddingBottom = undefined; // Ensure no padding otherwise
      }

      // --- Build VNode Tree ---

      // 1. Icon
      const iconNode = h('div', { class: 'node-icon' }, [
        h(ElIcon, null, () => h(nodeIcon.value)) // Pass component constructor to h
      ]);

      // 2. Label
      const labelContent = data.name
          ? h('strong', data.name)
          : data.nodeSemanticType === 'array' ? '[Array]'
              : data.nodeSemanticType === 'object' ? '{Object}'
                  : data.nodeSemanticType === 'comment' ? 'Comment'
                      : `[${data.nodeSemanticType || 'Unknown'}]`;
      const labelNode = h('div', { class: 'node-label' }, labelContent);

      // 3. Separator (conditional)
      const separatorNode = (data.name && displayValue.value !== null)
          ? h('div', { class: 'separator' }, ':')
          : null;

      // 4. Value (conditional)
      const valueNode = (displayValue.value !== null)
          ? h('div', { class: 'node-value' }, displayValue.value)
          : null;

      // 5. Attributes Area (conditional)
      let attributesAreaNode: VNode | null = null;
      if (canShowAttributes.value) {
        // 5a. Attribute Content Logic
        let attributeContentNode: VNode | VNode[] | null = null;
        const attrsArray = Object.entries(data.attributes).map(([name, value]) => ({ name, value: String(value) }));

        if (attributeDisplayMode === 'hide') {
          if (normalizeAppearanceWithAttrs && attributeCount.value > 0) {
            // Render content placeholder (approximate height of one line item)
            attributeContentNode = h('div', { class: 'attribute-renderer-placeholder', style: { height: '15px', visibility: 'hidden'} });
          } else {
            attributeContentNode = null;
          }
        } else if (attributeDisplayMode === 'count') {
          attributeContentNode = h('span', { class: 'attribute-summary' }, `(Attrs: ${attributeCount.value})`);
        } else { // 'first-1', 'first-3', 'all'
          const limit = attributeDisplayMode === 'first-1' ? 1 : attributeDisplayMode === 'first-3' ? 3 : Infinity;
          const itemsToRender = attrsArray.slice(0, limit);
          const showMore = attributeDisplayMode !== 'all' && attributeCount.value > limit;

          const attributeItemNodes = itemsToRender.map(attr =>
              h('div', { class: 'attribute-item', key: attr.name }, [
                h('span', { class: 'attr-name' }, attr.name),
                h('span', { class: 'attr-value', title: attr.value }, truncate(attr.value, 15))
              ])
          );

          if (showMore) {
            attributeItemNodes.push(h('span', { class: 'more-attrs', key: 'more-attrs' }, '...'));
          }
          attributeContentNode = h('div', { class: 'attribute-list' }, attributeItemNodes);
        }

        // 5b. Separator Line Style
        const separatorWrapperStyle: CSSProperties = {};
        if (attributeSeparatorStyle !== 'none') {
          separatorWrapperStyle.borderTopWidth = '1px';
          separatorWrapperStyle.borderTopStyle = attributeSeparatorStyle;
          separatorWrapperStyle.borderTopColor = 'var(--el-border-color)';
        } else {
          separatorWrapperStyle.borderTopWidth = '0px';
        }

        // 5c. Combine into wrapper (only render wrapper if content exists)
        if (attributeContentNode) {
          attributesAreaNode = h(
              'div',
              {
                class: 'attribute-display-wrapper', // Use the same class as template for styling
                style: separatorWrapperStyle // Apply border style here
              },
              [attributeContentNode] // Render the content
          );
        }
      }

      // 6. Node Content Container
      const nodeContent = h(
          'div',
          { class: 'node-content' },
          [iconNode, labelNode, separatorNode, valueNode, attributesAreaNode].filter(Boolean) // Filter out null nodes
      );

      // 7. Root Node Container
      return h(
          'div',
          {
            class: ['universal-node', nodeTypeClass.value, { 'is-selected': selected }],
            style: rootNodeStyles
          },
          [
            // Handles need to be rendered by Vue Flow's slot mechanism in the parent,
            // or manually created here if needed (more complex). Assuming parent handles them.
            // h(Handle, { type: 'target', position: Position.Left }),
            // h(Handle, { type: 'source', position: Position.Right }),
            nodeContent
          ]
      );
    };
  }
});
</script>

<style lang="scss" scoped>
/* Scoped styles specific to this node's structure, if any */
/* Borrow styles from UniversalNode.vue by class name */

/* It's generally better to extract shared styles to a common place */
/* or rely on the styles defined in UniversalNode.vue if this component */
/* is always used within its context via slots. */

/* Replicate styles from UniversalNode.vue for classes used in h() */
.universal-node {
  border: 1px solid rgba(0, 0, 0, 0.1);
  padding: 8px 12px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  min-width: 150px;
  font-size: 13px;
  position: relative;
  transition: background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease, padding-bottom 0.2s ease; /* Add padding transition */
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  /* padding-bottom applied dynamically */

  &.is-selected {
    border-color: var(--el-color-primary-dark-2);
    box-shadow: 0 0 0 2px var(--el-color-primary);
  }
}
.node-content {
  display: flex;
  align-items: center;
  gap: 5px;
  width: 100%;
  flex-wrap: wrap;
}
.node-icon {
  flex-shrink: 0;
  display: flex;
  align-items: center;
}
.node-label {
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100px;
}
.node-label strong {}
.node-label span {
  opacity: 0.85;
}
.separator {
  opacity: 0.7;
  margin: 0 4px;
}
.node-value {
  opacity: 0.9;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-grow: 1;
  max-width: 150px;
}

/* Wrapper for attributes */
.attribute-display-wrapper {
  width: 100%;
  padding-top: 6px; /* Space between border and content */
  margin-top: 6px;  /* Space between main content and border */
  line-height: 1;
  /* Dynamic border applied via style */
}

/* Styles for elements rendered by h() */
.attribute-summary {
  font-size: 0.9em;
  opacity: 0.8;
  display: block; /* Ensure it takes space */
}

.attribute-list {
  display: flex; /* Stack items vertically */
  flex-direction: column;
  gap: 2px; /* Vertical gap */
  align-items: flex-start;
  width: 100%;
}

.attribute-item {
  display: flex; /* Keep name/value inline */
  font-size: 0.9em;
  width: 100%;
  max-width: 100%;
  overflow: hidden;

  .attr-name {
    font-weight: 600;
    margin-right: 4px;
    color: var(--el-color-success);
    flex-shrink: 0;
    &::after {
      content: '=';
      margin-left: 2px;
      color: var(--el-text-color-regular);
      opacity: 0.8;
    }
  }

  .attr-value {
    color: var(--el-color-danger);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    opacity: 0.85;
    flex-grow: 1;
    text-align: left;
  }
}

.more-attrs {
  font-size: 0.9em;
  opacity: 0.8;
  margin-top: 2px;
}

.attribute-renderer-placeholder {
  box-sizing: border-box;
}

/* Dark mode adjustments */
.dark .universal-node {
  border-color: rgba(255, 255, 255, 0.15);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);

  &.is-selected {
    border-color: var(--el-color-primary-light-3);
    box-shadow: 0 0 0 2px var(--el-color-primary-light-3);
  }
}
.dark .attribute-display-wrapper {
  /* Adjust opacity or border color if needed */
  border-top-color: var(--el-border-color-lighter); // Example adjustment
}

</style>